#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/6/8 11:51
# @Author : linjinting
# @Project : custom_protocol
# @File : main.py
# @Software: PyCharm

import sys
import threading
from PySide2 import QtWidgets
from PySide2.QtCore import QRegExp
from PySide2.QtGui import QRegExpValidator

from UI_protocol import Ui_FormProtocol
from custom_widget import ComboCheckBox
from TTLLogging import create_logger
from distribute import LoggerDistribute
from bettersocket.tcp_client import TCPClient
from bettersocket.tcp_server import TCPServer
from protocol_message import produce_yx, produce_yk, parse
from mysql_handle import HandleBase, Station, Device, DeviceType
from unit.config import get_config, write_config
from unit.format import format_hex_for_bytes
from callback_handle import CallbackHandle


class Protocol(QtWidgets.QWidget, Ui_FormProtocol):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.bound_single()
        self.lineEdit_reg()
        self.init_ui_config()
        self.add_widget_combo()

        config_logger = get_config().get('logger')
        level = 'info'
        if config_logger:
            level = config_logger.get('level', 'info')
        self.m_logger_recv = create_logger('protocol.recv', level=level)
        self.m_logger_send = create_logger('protocol.send', level=level)

        self.m_recv_client = None
        self.m_recv_server = None
        self.m_send_client = None
        self.m_send_server = None
        self.m_send_opera = None

        self.m_mysql_conn = None
        self.m_devices = None
        self.station_filter_devices = None
        self.m_recv_distribute = LoggerDistribute(self.textEdit_recv, self.m_logger_recv)
        self.m_send_distribute = LoggerDistribute(self.textEdit_send, self.m_logger_send)

        self.m_recv_handle = CallbackHandle(distribute=self.m_recv_distribute)
        self.m_send_handle = CallbackHandle(distribute=self.m_send_distribute)

    def add_widget_combo(self):
        self.comboCheckBox_device = ComboCheckBox(self.widget_comboCheckBox)
        self.gridLayout_combo = QtWidgets.QGridLayout(self.widget_comboCheckBox)
        self.gridLayout_combo.addWidget(self.comboCheckBox_device, 0, 0, 1, 1)
        self.gridLayout_combo.setContentsMargins(0, 0, 0, 0)
        # self.comboCheckBox_device.editTextChanged.connect(self.filter_device_type)

    def lineEdit_reg(self):
        reg_num = QRegExp('[0-9]+$')
        # reg_str_num = QRegExp('[a-zA-Z0-9]+$')
        # validator_str_num = QRegExpValidator(self)
        # validator_str_num.setRegExp(reg_str_num)
        validator_num = QRegExpValidator(self)
        validator_num.setRegExp(reg_num)

        self.lineEdit_ID.setValidator(validator_num)
        self.lineEdit_port_recv.setValidator(validator_num)
        self.lineEdit_port_send.setValidator(validator_num)
        self.lineEdit_port_db.setValidator(validator_num)

    def init_ui_config(self):
        config = get_config()
        if not config:
            return
        #   config_mysql_ui
        mysql_ = config.get('mysql')
        if mysql_:
            self.lineEdit_host_db.setText(mysql_.get('host'))
            self.lineEdit_port_db.setText(mysql_.get('port'))
            self.lineEdit_user.setText(mysql_.get('user'))
            self.lineEdit_password.setText(mysql_.get('passwd'))
            self.lineEdit_db.setText(mysql_.get('db'))
            self.lineEdit_charset.setText(mysql_.get('charset'))
        #  config_recv
        recv_ = config.get('recv')
        if recv_:
            self.lineEdit_ip_recv.setText(recv_.get('ip'))
            self.lineEdit_port_recv.setText(recv_.get('port'))
        #  config_send
        send_ = config.get('send')
        if send_:
            self.lineEdit_ip_send.setText(send_.get('ip'))
            self.lineEdit_port_send.setText(send_.get('port'))
        # other logger
        logger_ = config.get('logger')
        if logger_:
            self.comboBox_logger_lever.setCurrentText(logger_.get('level'))

    def bound_single(self):
        self.pushButton_recv_client.clicked.connect(self.recv_connect)
        self.pushButton_recv_listen.clicked.connect(self.recv_listen)

        self.pushButton_send_client.clicked.connect(self.send_connect)
        self.pushButton_send_listen.clicked.connect(self.send_listen)
        self.pushButton_send.clicked.connect(self.send_msg)
        self.pushButton_produce_msg.clicked.connect(self.produce_msg)
        self.pushButton_get_data.clicked.connect(self.get_mysql_data)
        self.tableWidget_device.clicked.connect(self.get_deviceID)

        self.comboBox_station.currentIndexChanged.connect(self.filter_device)
        self.comboBox_type.currentIndexChanged.connect(self.produce_msg)
        self.pushButton_sql_connect.clicked.connect(self.mysql_connect)
        self.pushButton_reload.clicked.connect(self.reload_config)
        self.pushButton_save.clicked.connect(self.save_config)

    def recv_connect(self):
        def recv_client_connected():
            self.pushButton_recv_client.setText('断开连接')
            self.pushButton_recv_client.setEnabled(True)

        def recv_client_close():
            self.m_recv_client.close()
            self.m_recv_client = None
            self.pushButton_recv_client.setText('连接')
            self.pushButton_recv_client.setEnabled(True)
            self.pushButton_recv_listen.setEnabled(True)
            self.m_recv_handle.unregister('close')
            self.m_recv_handle.unregister('connect')
            self.m_recv_handle.unregister('read')

        if self.m_recv_client:
            recv_client_close()
            return
        config_ = self.get_ui_param('recv')
        channel = 'yx' if self.comboBox_channel.currentIndex() == 0 else 'yk'
        if config_ is None:
            return
        self.m_recv_handle.register('close', recv_client_close)
        self.m_recv_handle.register('connect', recv_client_connected)
        self.m_recv_handle.register('read', self.reply_msg)

        self.pushButton_recv_client.setEnabled(False)
        self.pushButton_recv_listen.setEnabled(False)

        self.m_recv_client = TCPClient((config_['ip'], config_['port']),
                                       channel=channel, callback=self.m_recv_handle.callback)

        th = threading.Thread(target=self.m_recv_client.start)
        th.start()

    def recv_listen(self):
        def recv_server_listen():
            self.pushButton_recv_listen.setText('停止')
            self.pushButton_recv_listen.setEnabled(True)

        def recv_server_close():
            self.m_recv_server.close()
            self.m_recv_server = None
            self.pushButton_recv_listen.setText('监听')
            self.pushButton_recv_client.setEnabled(True)
            self.pushButton_recv_listen.setEnabled(True)
            self.m_recv_handle.unregister('sclose')
            self.m_recv_handle.unregister('listen')
            self.m_recv_handle.unregister('connect')
            self.m_recv_handle.unregister('read')

        def recv_server_connect():
            pass

        def recv_client_close():
            pass

        if self.m_recv_server:
            recv_server_close()
            return
        config_ = self.get_ui_param('recv')
        if config_ is None:
            return
        channel = 'yx' if self.comboBox_channel.currentIndex() == 0 else 'yk'
        self.m_recv_handle.register('listen', recv_server_listen)
        self.m_recv_handle.register('connect', recv_server_connect)
        self.m_recv_handle.register('sclose', recv_server_close)
        self.m_recv_handle.register('cclose', recv_client_close)
        self.m_recv_handle.register('read', self.reply_msg)

        self.pushButton_recv_client.setEnabled(False)
        self.pushButton_recv_listen.setEnabled(False)

        self.m_recv_server = TCPServer((config_['ip'], config_['port']), channel=channel,
                                       callback=self.m_recv_handle.callback)

        threading.Thread(target=self.m_recv_server.start).start()

    def send_connect(self):
        def send_client_connected():
            self.pushButton_send_client.setText('断开连接')
            self.pushButton_send_client.setEnabled(True)

        def send_client_close():
            self.m_send_client.close()
            self.m_send_opera = self.m_send_client = None
            self.pushButton_send_client.setText('连接')
            self.pushButton_send_client.setEnabled(True)
            self.pushButton_send_listen.setEnabled(True)
            self.m_send_handle.unregister('connect')
            self.m_send_handle.unregister('close')
            self.m_send_handle.unregister('read')

        if self.m_send_client:
            send_client_close()
            return
        config_ = self.get_ui_param('send')
        if config_ is None:
            return
        channel = 'yx' if self.comboBox_type.currentIndex() == 0 else 'yk'
        self.m_send_handle.register('connect', send_client_connected)
        self.m_send_handle.register('close', send_client_close)
        self.m_send_handle.register('read', self.reply_msg)

        self.pushButton_send_client.setEnabled(False)
        self.pushButton_send_listen.setEnabled(False)
        self.m_send_opera = self.m_send_client = TCPClient((config_['ip'], config_['port']), channel=channel,
                                                           callback=self.m_send_handle.callback)
        threading.Thread(target=self.m_send_client.start).start()

    def send_listen(self):
        def send_server_listen():
            self.pushButton_send_listen.setText('停止')
            self.pushButton_send_listen.setEnabled(True)

        def send_server_connected():
            pass

        def send_server_close():
            self.m_send_server.close()
            self.m_send_opera = self.m_send_server = None
            self.pushButton_send_listen.setText('监听')
            self.pushButton_send_client.setEnabled(True)
            self.pushButton_send_listen.setEnabled(True)
            self.m_send_handle.unregister('listen')
            self.m_send_handle.unregister('connect')
            self.m_send_handle.unregister('sclose')
            self.m_send_handle.unregister('cclose')
            self.m_send_handle.unregister('read')

        def send_client_close():
            pass

        if self.m_send_server:
            send_server_close()
            return
        config_ = self.get_ui_param('send')
        if config_ is None:
            return
        channel = 'yx' if self.comboBox_type.currentIndex() == 0 else 'yk'
        self.m_send_handle.register('listen', send_server_listen)
        self.m_send_handle.register('connect', send_server_connected)
        self.m_send_handle.register('sclose', send_server_close)
        self.m_send_handle.register('cclose', send_client_close)
        self.m_send_handle.register('read', self.reply_msg)

        self.pushButton_send_listen.setEnabled(False)
        self.pushButton_send_client.setEnabled(False)
        self.m_send_opera = self.m_send_server = TCPServer((config_['ip'], config_['port']), channel=channel,
                                                           callback=self.m_send_handle.callback)

        threading.Thread(target=self.m_send_server.start).start()

    # def reply_msg(self, **kwargs):
    #     conn = kwargs.get('conn')
    #     msg = kwargs.get('msg')
    #     isReply = kwargs.get('isReply')
    #     if isReply:
    #         conn.send(msg)

    def reply_msg(self, **kwargs):
        if not self.checkBox_isReply.isChecked():
            return
        channel = kwargs.get('channel')
        data = kwargs.get('data')
        kwargs['type'] = 'logger'
        if not channel or not data:
            kwargs['msg'] = '[protocol] channel or data is None'
            # self.m_recv_handle.callback(**kwargs)
            return
        JZS = parse(data, channel)
        if isinstance(JZS, str):
            print('reply parse stuck is %s' % JZS)
            return
        # print('reply state', JZS.state)
        if JZS.state in [0x80, 0x81]:
            # print('return')
            return
        send_msg = produce_yk(JZS.ID, JZS.commandType, JZS.state | 0x80)

        conn = kwargs.get('client')
        server = kwargs.get('server')
        # print('reply', send_msg, conn, server)
        if conn and server:
            server.send_msg_to_client(conn, send_msg, True)
        elif conn and isinstance(conn, TCPClient):
            # print('send----------------------')
            # conn.set_channel('yk')
            conn.send_msg(send_msg, True)
        else:
            print('reply pass')

    def send_msg(self):
        msg = self.plainTextEdit_send_msg.toPlainText()
        channel = 'yx' if self.comboBox_type.currentIndex() == 0 else 'yk'
        self.m_send_opera.set_channel(channel)

        data = bytes.fromhex("".join(msg.split(' ')))
        if not self.m_send_opera:
            self.m_send_distribute.log('未连接服务端,或服务端未监听', 'info')
            return
        self.m_send_opera.send_msg(data)

    def produce_msg(self):
        ID = int(self.lineEdit_ID.text())
        commandType = self.comboBox_type.currentIndex() | 4
        # print(commandType)
        state = int(self.comboBox_state.currentText())
        if commandType == 4:
            send_msg = produce_yx(ID, commandType, state)
        else:
            send_msg = produce_yk(ID, commandType, state)
        self.plainTextEdit_send_msg.setPlainText(format_hex_for_bytes(send_msg))

    def get_mysql_data(self):
        if self.m_mysql_conn is None:
            self.mysql_connect()

        device = Device()
        device.set_connection(self.m_mysql_conn)
        self.m_devices = device.get_device()

        deviceType = DeviceType()
        deviceType.set_connection(self.m_mysql_conn)
        deviceTypes = deviceType.get_deviceType()
        self.comboCheckBox_device.add_items(deviceTypes)

        station = Station()
        station.set_connection(self.m_mysql_conn)
        ret = station.get_station()
        self.set_comboBox_station(ret)

        # # 主动关闭
        # self.mysql_connect()

    def get_deviceID(self):
        items = self.tableWidget_device.selectedItems()
        device_id = self.tableWidget_device.item(items[0].row(), 0)
        self.lineEdit_ID.setText(device_id.text())
        self.produce_msg()

    def set_comboBox_station(self, stations):
        for station in stations:
            self.comboBox_station.addItem(station['NAME'].decode(), station)

    def filter_device(self, index):
        if index == 0:
            self.station_filter_devices = self.m_devices
        else:
            station = self.comboBox_station.itemData(index)
            station['NAME'] = station['NAME'].decode()
            self.m_send_distribute.log(msg='[filter.device] station :%s' % station, level='info')
            count = len(self.m_devices)
            self.station_filter_devices = [self.m_devices[r] for r in range(count) if
                                           self.m_devices[r]['stationID'] == station['ID']]
        self.set_table_device(self.station_filter_devices)

    def filter_device_type(self):
        # deviceTypes = self.comboCheckBox_device.deviceTypeID
        count = len(self.m_devices)
        self.station_filter_devices = [self.m_devices[r] for r in range(count) if
                                       self.m_devices[r]['deviceTypeID'] in self.comboCheckBox_device.deviceTypeID]

        self.set_table_device(self.station_filter_devices)

    def set_table_device(self, devices):
        rows = len(devices)
        self.tableWidget_device.setColumnCount(3)
        self.tableWidget_device.setRowCount(rows)
        self.tableWidget_device.setHorizontalHeaderLabels(['ID', 'stationID', 'number'])

        for row in range(rows):
            item_id = QtWidgets.QTableWidgetItem()
            item_id.setText(str(devices[row]['ID']))
            self.tableWidget_device.setItem(row, 0, item_id)

            item_stationID = QtWidgets.QTableWidgetItem()
            item_stationID.setText(str(devices[row]['stationID']))
            self.tableWidget_device.setItem(row, 1, item_stationID)

            item_number = QtWidgets.QTableWidgetItem()
            item_number.setText(devices[row]['number'].decode())
            self.tableWidget_device.setItem(row, 2, item_number)

    def mysql_connect(self):
        def mysql_client_connect():
            self.pushButton_sql_connect.setText('断开连接')
            self.pushButton_sql_connect.setEnabled(True)
            self.pushButton_get_data.setEnabled(True)

        def mysql_client_close():
            self.pushButton_sql_connect.setText('连接')
            self.m_mysql_conn.close()
            self.m_mysql_conn = None
            self.pushButton_sql_connect.setEnabled(True)
            self.pushButton_get_data.setEnabled(True)
            self.m_send_handle.unregister('mysql_connect')
            self.m_send_handle.unregister('mysql_close')

        if self.m_mysql_conn and self.m_mysql_conn.isConnected():
            mysql_client_close()
            return
        config_ = self.get_ui_param('mysql')
        if config_ is None:
            return

        self.m_send_handle.register('mysql_connect', mysql_client_connect)
        self.m_send_handle.register('mysql_close', mysql_client_connect)

        self.pushButton_sql_connect.setEnabled(False)
        self.pushButton_get_data.setEnabled(False)

        hb = HandleBase(config_, callback=self.m_send_handle.callback)
        self.m_mysql_conn = hb.get_connection()
        # if hb.isConnected():
        #     self.m_send_distribute.log(msg='connect mysql success:%s' % config_, level='info')
        #     self.m_mysql_conn = hb.get_connection()
        #     self.pushButton_sql_connect.setText('断开连接')
        # else:
        #     self.m_send_distribute.log(msg='connect mysql failed:%s' % config_, level='info')
        #     self.mysql_client_close()

    def get_ui_param(self, section=None):
        config_mysql = {
            'host': self.lineEdit_host_db.text(),
            'port': self.lineEdit_port_db.text(),
            'user': self.lineEdit_user.text(),
            'passwd': self.lineEdit_password.text(),
            'db': self.lineEdit_db.text(),
            'charset': self.lineEdit_charset.text()
        }
        config_recv = {
            'ip': self.lineEdit_ip_recv.text(),
            'port': self.lineEdit_port_recv.text()
        }
        config_send = {
            'ip': self.lineEdit_ip_send.text(),
            'port': self.lineEdit_port_send.text()
        }
        config_logger = {'level': self.comboBox_logger_lever.currentText()}
        config = {
            'mysql': config_mysql,
            'recv': config_recv,
            'send': config_send,
            'logger': config_logger
        }

        if section:
            config_ = config.get(section)
            try:
                config_['port'] = int(config_['port'])
                return config_
            except Exception as e:
                self.error_messageBox('%s,as port' % e.__str__())
                return
        return config

    def reload_config(self):
        # print(get_config())
        self.init_ui_config()

    def save_config(self):
        write_config(self.get_ui_param())

    def error_messageBox(self, msg):
        QtWidgets.QMessageBox.about(self, '错误', msg)

    def closeEvent(self, event):
        reply = QtWidgets.QMessageBox.question(self, 'warring', '是否要退出程序',
                                               QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes,
                                               QtWidgets.QMessageBox.No)
        if reply == QtWidgets.QMessageBox.Yes:
            self.__del__()
            event.accept()
        else:
            event.ignore()

    def __del__(self):
        try:
            if self.m_recv_server:
                self.m_recv_server.close()
            if self.m_send_server:
                self.m_send_server.close()

            if self.m_recv_client:
                self.m_recv_client.close()

            if self.m_send_client:
                self.m_send_client.close()
        except OSError:
            pass


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    form = Protocol()
    form.show()
    sys.exit(app.exec_())
